﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace MergeLocBamlCsv
{
    class Program
    {
        static readonly string[] _ValidTypes = {
                                            "Title",
                                            "ToolTip",
                                            "$Content",
                                            "Header",
                                            "Content",
                                            "Text",
                                            "Label"
                                          };
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("MergeLocBamlCsv V 1.00");
                Console.WriteLine("Usage: MergeLocBamlCsv <translated csv> <new csv>");
                Console.WriteLine("  <translated csv> CSV translated in the new language.");
                Console.WriteLine("  <new csv>        CSV generated from the current app.");
                return;
            }

            Console.WriteLine(string.Format("Merging {0} with {1}", args[0], args[1]));
            Dictionary<string, string> languageParts = null;
            try
            {
                languageParts = ParseOriginal(args[0]);
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }

            try
            {
                MergeOriginal(args[1], languageParts);
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Console.WriteLine("The file could not be merged:");
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }
        }

        /// <summary>
        /// Extract the translated text parts from the last time.
        /// Place them in a dictionary for later use.
        /// </summary>
        /// <param name="fileOriginal">Current translated file (LocBaml CSV file)</param>
        /// <returns>Dictionary of text parts in the correct language.</returns>
        private static Dictionary<string, string> ParseOriginal(string fileOriginal)
        {
            Dictionary<string, string> languageParts = new Dictionary<string, string>();

            // Create an instance of StreamReader to read from a file.
            // The using statement also closes the StreamReader.
            using (StreamReader sr = new StreamReader(fileOriginal))
            {
                String line;
                while ((line = sr.ReadLine()) != null)
                {
                    // Split the line into 7 parts and check if it's translatable.
                    string[] parts = line.Split(new char[] { ',' }, 7);
                    if ((parts.Length == 7) && (IsTranslatableLine(parts) == true))
                    {
                        var key=parts[0] + "," + parts[1];
                        var val=parts[6];
                        if (languageParts.Where(l => l.Key == key).Count()==0)
                            languageParts.Add(key, val);
                    }
                }
            }
            return languageParts;
        }

        // Test if a line contains a language part
        // - The columns 3 and 4 must be 'true'
        // - column 6 is filled but doesn't start with a # (this is a link)
        // - Contains a type of data we want to translate.
        // - Or we have an image source.
        private static bool IsTranslatableLine(string[] parts)
        {
            string[] names = parts[1].Split(new char[] { '.' });
            string lastName = names[names.Length - 1];

            return (((string.Compare(parts[3], "true", true) == 0) &&
                        (string.Compare(parts[4], "true", true) == 0) &&
                        (string.IsNullOrEmpty(parts[6]) == false) &&
                        (parts[6][0] != '#') &&
                        (_ValidTypes.Contains(lastName) == true)));
        }


        /// <summary>
        /// Merge the tranlated file and the newly build CSV in the base culture.
        /// The fileMerge is the source of the parse and will be overwritten.
        /// </summary>
        /// <param name="fileMerge">The translated file.</param>
        /// <param name="fileOriginal">The new generated CSV file</param>
        /// <param name="languageParts">List of translated text parts</param>
        private static void MergeOriginal(string fileMerge, Dictionary<string, string> languageParts)
        {
            FileAttributes attributes = File.GetAttributes(fileMerge);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                // Remove Readonly attr, to overwrite file
                attributes -= FileAttributes.ReadOnly;
                File.SetAttributes(fileMerge, attributes);
            }

            string fileNew = "out.csv";
            using (StreamWriter sw = new StreamWriter(fileNew, false, Encoding.GetEncoding("UTF-8")))
            {
                using (StreamReader sr = new StreamReader(fileMerge,Encoding.Unicode))
                {
                    String line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        string[] parts = line.Split(new char[] { ',' }, 7);
                        if ((parts.Length == 7) && (IsTranslatableLine(parts) == true))
                        {
                            // If it can be translated, we must be able find it in our bag of text parts.
                            string newTextPart;
                            var key=parts[0] + "," + parts[1];
                            //bool flFound = languageParts.TryGetValue(key, out newTextPart);
                            bool flFound = find(key, out newTextPart, languageParts);
                            if (flFound == true)
                            {
                                parts[6] = newTextPart;
                                line = string.Join(",", parts);
                            }
                        }
                        WriteColumn(line);
                        sw.WriteLine(line);
                    }
                }
            }
        }
        private static void WriteColumn(string value)
        {
            if (value == null)
                value = string.Empty;

            // if it contains delimeter, quote, newline, we need to escape them
            if (value.IndexOfAny(new char[] { '\"', '\r', '\n', ',' }) >= 0)
            {
                // make a string builder at the minimum required length;
                StringBuilder builder = new StringBuilder(value.Length + 2);

                // put in the opening quote
                builder.Append('\"');

                // double quote each quote
                for (int i = 0; i < value.Length; i++)
                {
                    builder.Append(value[i]);
                    if (value[i] == '\"')
                    {
                        builder.Append('\"');
                    }
                }

                // put in the closing quote
                builder.Append('\"');
                value = builder.ToString();
            }
        }
        private static bool find(string keyInfo, out string ret, Dictionary<string, string> d)
        {
            bool result=false;
            ret = null;
            foreach (var dd in d)
            {
                string[] toParts = keyInfo.Split(new char[] { ',' }, 2);
                string[] fromParts = dd.Key.Split(new char[] { ',' }, 2);

                var toBaml = ExtractBaml(toParts[0]);
                var fromBaml = ExtractBaml(fromParts[0]);
                var toUid = toParts[1];
                var fromUid = fromParts[1];

                if (toBaml == fromBaml && toUid == fromUid)
                {
                    ret = dd.Value;
                    return true;
                }
            }
            return result;
        }

        private static string ExtractBaml(string toParts)
        {
            string result=string.Empty;
            string[] to = toParts.Split(new char[] { '.' });
            int len=to.Length-2;
            string tmp = to[len];
            if (tmp.IndexOf(':') > 0)
            {
                string[] to2 = tmp.Split(new char[] { ':' });
                result = to2[to2.Length - 1];
            }
            else
            {
                result = to[len];
            }
            return result;
        }
    }
}
